/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package geneontofx.math;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.HashMap;

/**
 *
 * @author loskov
 */
public class Binomial {
    
    private static MathContext MATH_CONTEXT = new MathContext(16, RoundingMode.CEILING);
    private static HashMap<String, BigDecimal> binomialDecimalCacheMap = new HashMap<>();
    private static HashMap<String, BigDecimal> binomialLogCacheMap = new HashMap<>();
    
    public static enum BinomialMethod {
        DECIMAL, LOG
    }
    
    private BigDecimal result;
    private BinomialMethod type;

    public Binomial() {
    }
    
    public BigDecimal binomialDecimal(Integer n, Integer k) {
        String binomialId = n+","+k;
        type = BinomialMethod.DECIMAL;
        if(binomialDecimalCacheMap.containsKey(binomialId)) {
            return  binomialDecimalCacheMap.get(binomialId);
        }
        if(n-k < k) {
            return binomialDecimal(n, n-k);
        }
        else if(k == 0 || k == n) {
            return BigDecimal.ONE;
        }
        else if(k == 1) {
            return new BigDecimal(n);
        }
        else {
            BigDecimal pochhammer = new Factorial().pochhammerFallFact(n, k);
            BigDecimal factK = new Factorial().factDecimalCached(k);
            result = pochhammer.divide(factK, MATH_CONTEXT);
            
            binomialDecimalCacheMap.put(binomialId, result);
            return result;
        }
    }
    
    public BigDecimal binomialLog(Integer n, Integer k) {
        String binomialId = n+","+k;
        if(binomialLogCacheMap.containsKey(binomialId)) {
            return  binomialLogCacheMap.get(binomialId);
        }
        else {
            type = BinomialMethod.LOG;
            BigDecimal logNFact = new Factorial().factLogCached(n);
            BigDecimal logKFact = new Factorial().factLogCached(k);
            BigDecimal logNKFact = new Factorial().factLogCached(n-k);
            result = logNFact.subtract(logKFact.add(logNKFact));
            binomialLogCacheMap.put(binomialId, result);
            return result;
        }
    }

    @Override
    public String toString() {
        return "Binomial{" + "result=" + result + ", type=" + type + '}';
    }
    
}
